package user

import (
	"github.com/beego/beego/v2/core/validation"
	"huijing_mall/internal/cachebase"
	"huijing_mall/internal/database"
	"huijing_mall/internal/database/models"
	"huijing_mall/logic/userlogic"
	"huijing_mall/shared/common"
	"huijing_mall/shared/get_ctx_value"
	"huijing_mall/shared/kernel/ask"
	"huijing_mall/shared/kernel/hmErrors"
	"huijing_mall/variable"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"huijing_mall/pkg/logger"

	"github.com/bytedance/sonic"
	"github.com/gin-gonic/gin"
)

// @Summary		登录接口
// @Description	登录接口
// @Accept			json
// @Produce		json
// @Success		200	{object}	echo.Response
// @Router			/api/v1/login [post]
func Login(c *gin.Context) { //登录
	userIp := c.ClientIP()
	logger.Info(userIp)
	var req ask.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		// 如果解析失败，返回错误
		ask.Errors(c, http.StatusBadRequest, err)
		return
	}
	if req.LoginType != "0" && req.LoginType != "1" {
		ask.Errors(c, http.StatusBadRequest, hmErrors.ErrSystem("登录参数错误"))
		return
	}
	//暂时 使用beego框里面的验证器吧 ☺☺☺☺
	variable.Valid = validation.Validation{}
	//后期要用
	var mobile, email *string
	if req.LoginType == "0" { //手机号验证码 登录
		variable.Valid.Required(req.Sms, "sms").Message("验证码不可以为空")
		variable.Valid.Required(req.Link, "link").Message("手机号或邮箱不可以为空")
		if req.Link != "" {
			if strings.Contains(req.Link, "@") {
				variable.Valid.Email(req.Link, "email").Message("邮箱格式有误")
				email = &req.Link
			} else {
				variable.Valid.Mobile(req.Link, "mobile").Message("手机号格式有误")
				mobile = &req.Link
			}
		}
		if variable.Valid.HasErrors() {
			for _, v := range variable.Valid.Errors {
				ask.Errors(c, http.StatusBadRequest, v)
				return
			}
		}
	} else {
		variable.Valid.Required(req.Username, "username").Message("用户名不可以为空")
		variable.Valid.Required(req.Password, "password").Message("密码不可以为空")
		variable.Valid.Required(req.Sms, "Sms").Message("密码不可以为空")
		if variable.Valid.HasErrors() {
			for _, v := range variable.Valid.Errors {
				ask.Errors(c, http.StatusBadRequest, v)
				return
			}
		}
	}
	userlogic.LoginLogic(c, req, mobile, email)
}

// Register
func Register(c *gin.Context) {
	// 定义一个结构体变量
	var req ask.RegRequest
	// 从请求体中解析 JSON 数据
	if err := c.ShouldBindJSON(&req); err != nil {
		// 如果解析失败，返回错误
		ask.NewErrors(c, http.StatusBadRequest, -1, err)
		return
	}
	//暂时 使用beego框里面的验证器吧 ☺☺☺☺
	variable.Valid = validation.Validation{}
	var mobile, email *string
	//验证 错误信息
	{
		variable.Valid.Required(req.Sms, "Sms").Message("验证码不可以为空")
		variable.Valid.Required(req.Link, "mobile").Message("手机号或邮箱不可以为空")
		variable.Valid.Required(req.Nickname, "nickname").Message("昵称不可以为空")
		variable.Valid.Required(req.Password, "password").Message("密码不可以为空")
		if req.Link != "" {
			if strings.Contains(req.Link, "@") {
				variable.Valid.Email(req.Link, "email").Message("邮箱格式有误")
				email = &req.Link
			} else {
				variable.Valid.Mobile(req.Link, "mobile").Message("手机号格式有误")
				mobile = &req.Link
			}
		}
		if variable.Valid.HasErrors() {
			for _, v := range variable.Valid.Errors {
				ask.NewErrors(c, http.StatusBadRequest, -1, v)
				return
			}
		}
	}

	userlogic.RegLogic(c, req, mobile, email)
	return
}

// Register
func UpdatePwd(c *gin.Context) {
	// 定义一个结构体变量
	var req ask.UpPwdRequest
	// 从请求体中解析 JSON 数据
	if err := c.ShouldBindJSON(&req); err != nil {
		// 如果解析失败，返回错误
		ask.NewErrors(c, http.StatusBadRequest, -1, err)
		return
	}
	//暂时 使用beego框里面的验证器吧 ☺☺☺☺
	variable.Valid = validation.Validation{}
	variable.Valid.Required(req.Username, "username").Message("用户名不可以为空")
	variable.Valid.Required(req.Password, "password").Message("密码不可以为空")
	variable.Valid.Required(req.Link, "mobile").Message("手机号不可以为空")
	variable.Valid.Required(req.Sms, "sms").Message("验证码不可以为空")
	if strings.Contains(req.Link, "@") {
		variable.Valid.Email(req.Link, "email").Message("邮箱或手机号格式有误")
	} else {
		variable.Valid.Mobile(req.Link, "mobile").Message("手机号或邮箱格式有误")
	}
	if variable.Valid.HasErrors() {
		for _, v := range variable.Valid.Errors {
			// 如果解析失败，返回错误
			// 如果解析失败，返回错误
			ask.NewErrors(c, http.StatusBadRequest, -1, v)
			return
		}
	}
	userlogic.UpPwdLogic(c, req)
	return
}

// LoginOut
func LoginOut(c *gin.Context) {
	userlogic.LoginOut(get_ctx_value.GetDBS(c), get_ctx_value.GetUID(c))
	ask.Success(c, nil)
	return
}

// UploadAvatar
func UploadAvatar(c *gin.Context) {
	userID := get_ctx_value.GetUID(c)
	dbs := get_ctx_value.GetDBS(c)
	//文件
	file, err := c.FormFile("file")
	if err != nil {
		// 如果解析失败，返回错误
		ask.NewErrors(c, http.StatusBadRequest, -1, err)
		return
	}
	// 1.筛一下文件 并 构建根目录
	suffix := common.GetFileExtension(file.Filename, &common.FileExtensionOptions{WithDot: true, ToLower: true, MultipleDots: true})
	var src string
	switch suffix {
	case ".jpg", ".jpeg", ".png":
		// 验证文件名
		{
			if file.Filename == "" || strings.Contains(file.Filename, "/") || strings.Contains(file.Filename, "\\") {
				ask.NewErrors(c, http.StatusBadRequest, -1, hmErrors.FileNameErr)
				return
			}
			if file.Size > variable.MaxFileSize*2.5 { // 5MB限制
				ask.NewErrors(c, http.StatusBadRequest, -1, hmErrors.FileSizeErr)
				return
			}

			if !strings.HasPrefix(file.Header.Get("Content-Type"), "image/") {
				ask.NewErrors(c, http.StatusBadRequest, -1, hmErrors.FileTypeErr)
				return
			}
		}
		src = variable.UploadImgSrc
	case ".pdf", ".pdf.pdf":
		src = variable.UploadPDFSrc
	case ".xlsx":
		src = variable.UploadXlsxSrc
	default:
		src = variable.UploadOtherSrc
	}
	// 2. 打开文件
	f, err := file.Open()
	if err != nil {
		logger.Error(err.Error())
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemFileOpenErr)
		return
	}
	defer func(f multipart.File) {
		err = f.Close()
		if err != nil {

		}
	}(f)

	// 3. 读取文件内容
	fileBytes, _ := io.ReadAll(f)

	//输出路径
	uploadDir := filepath.Join("./", src)
	if !common.PathExists(uploadDir) {
		if err = os.MkdirAll(uploadDir, 0755); err != nil {
			logger.Error(err.Error())
			ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.MkdirFileErr)
			return
		}
	}

	//文件名 自定义
	var filename string
	filename = time.Now().Format("20060102150405")
	if len(file.Filename) > 5 {
		filename += common.RandomString(10) + suffix
	} else {
		filename += filepath.Ext(file.Filename)
	}

	// 4. 构造完整文件路径
	filePath := filepath.Join(uploadDir, filename)
	//最终路径
	path := src + filename

	// 5. 写入文件
	if err = os.WriteFile(filePath, fileBytes, 0644); err != nil {
		logger.Error(err.Error())
		ask.Errors(c, http.StatusInternalServerError, hmErrors.SaveFleErr)
		return
	}

	//暂时 这样写 img（头像） 才去操作库 其余 可以直接返回
	if src == variable.UploadImgSrc {

		//6.入库更改 路径
		if userID != 0 {
			//更改用户表
			if err = database.UpdateUserPhoto(dbs.DB, userID, path); err != nil {
				os.Remove(filePath)
				logger.Error(err.Error())
				ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
				return
			}
			//查询所有历史
			history, err1 := database.GetHistory(dbs.DB, userID)
			if err1 != nil {
				os.Remove(filePath)
				logger.Error(err.Error())
				ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
				return
			}
			//更改状态 为0
			for _, v := range history {
				err = database.UpdateHistory(dbs.DB, v.BaseModel.ID)
				if err != nil {
					os.Remove(filePath)
					logger.Error(err.Error())
					ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
					return
				}
			}
			//再插入一条最新的
			if err = database.SaveHistory(dbs.DB, models.UserAvatarHistory{
				UserID:    userID,
				AvatarURL: path,
				IsCurrent: true,
			}); err != nil {
				os.Remove(filePath)
				logger.Error(err.Error())
				ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
				return
			}

			userdataKey := common.StrPinJie(common.IntToStr(int(userID)), variable.UserDtaKey)
			//更新缓存
			var res string
			res, err = cachebase.RedisGet(dbs.Rdb, userdataKey)
			if err != nil {
				os.Remove(filePath)
				logger.Error(err.Error())
				ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
				return
			}
			var userInfo models.User
			sonic.Unmarshal([]byte(res), &userInfo)
			userInfo.Avatar = path
			//保持 时间不变
			err = cachebase.RedisSetKeepTTL(dbs.Rdb, userdataKey, userInfo)
			if err != nil {
				os.Remove(filePath)
				logger.Error(err.Error())
				ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
				return
			}
		}
	}
	// 7. 返回成功响应
	ask.Success(c, path)
	return
}

func GetUserInfo(c *gin.Context) {
	userID := get_ctx_value.GetUID(c)
	dbs := get_ctx_value.GetDBS(c)

	if userID != 0 {
		userInfo, err := ByUidGetUserInfo(dbs, userID)
		if err != nil {
			ask.NewErrors(c, http.StatusInternalServerError, -1, err)
			return
		}
		decrypt, err := common.Decrypt(variable.PwdKeyNew, userInfo.Password)
		if err != nil {
			ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.DecryptPwdErr)
			return
		}
		if decrypt != "Aa666888." {
			decrypt = ""
		}
		ask.Success(c, userInfo)
		return
	}
	ask.Errors(c, http.StatusOK, hmErrors.ErrMsg("请先登录"))
	return
}

func ByUidGetUserInfo(dbs get_ctx_value.DBS, uid uint) (userInfo *models.User, err error) {
	userdataKey := common.StrPinJie(strconv.Itoa(int(uid)), variable.UserDtaKey)
	userCURD := models.NewModelCRUD(models.UserType)
	var res string
	if cachebase.RedisExist(dbs.Rdb, userdataKey) {
		res, err = cachebase.RedisGet(dbs.Rdb, userdataKey)
		if err != nil {
			logger.Error("cachebase userdata nil", err.Error())
			return &models.User{}, hmErrors.SystemTimeOutErr
		}
		sonic.Unmarshal([]byte(res), &userInfo)
	} else {
		err = userCURD.GetByID(dbs.DB, uid)
		if err != nil {
			logger.Error(err.Error())
			return &models.User{}, hmErrors.SystemTimeOutErr
		}
		userInfo, err = models.AssertModelCRUD[*models.User](userCURD)
		if err != nil {
			logger.Error(err.Error())
			return &models.User{}, hmErrors.SystemTypeErr
		}
	}
	return userInfo, nil
}

func AddAddress(c *gin.Context) {
	userID := get_ctx_value.GetUID(c)
	dbs := get_ctx_value.GetDBS(c)
	userACRUD := models.NewModelCRUD(models.UserAddressType)
	userCRUD := models.NewModelCRUD(models.UserType)

	// 定义一个结构体变量
	var req ask.AddAddress
	// 从请求体中解析 JSON 数据
	if err := c.ShouldBindJSON(&req); err != nil {
		// 如果解析失败，返回错误
		ask.NewErrors(c, http.StatusBadRequest, -1, err)
		return
	}
	var isDefault bool
	switch req.IsDefault {
	case "1":
		isDefault = true
	case "0":
		isDefault = false
	default:
		// 如果解析失败，返回错误
		ask.NewErrors(c, http.StatusBadRequest, -1, hmErrors.UnknowDataErr)
		return
	}

	err := userCRUD.GetByID(dbs.DB, userID)
	if err != nil {
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTimeOutErr)
		return
	}
	//类型断言
	crud, AErr := models.AssertModelCRUD[*models.User](userCRUD)
	if AErr != nil {
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTypeErr)
		return
	}
	userACRUD = &models.UserAddress{
		UserID:    userID,
		Receiver:  req.Receiver,
		Phone:     req.Phone,
		Province:  req.Province,
		City:      req.City,
		District:  req.District,
		Detail:    req.Detail,
		IsDefault: isDefault,
		User:      *crud,
	}
	err = userACRUD.Create(dbs.DB)
	if err != nil {
		ask.Errors(c, http.StatusInternalServerError, err)
		return
	}

	ask.Success(c, nil)
	return
}

func FindAddress(c *gin.Context) {
	dbs := get_ctx_value.GetDBS(c)
	userACRUD := models.NewModelCRUD(models.UserAddressType)

	list, total, err := userACRUD.List(dbs.DB, 1, 10)
	if err != nil {
		ask.NewErrors(c, http.StatusInternalServerError, -1, hmErrors.SystemTypeErr)
		return
	}
	//address := database.GetUserAddress(dbs.DB, userID)
	data := make(map[string]interface{})
	data["list"] = list
	data["total"] = total
	ask.Success(c, data)
	return
}
